package worker

import (
	"context"
	"fmt"
	"github.com/coreos/etcd/clientv3"
	"go-crontab/common"
	"net"
	"strings"
	"time"
)

type (
	// 节点注册
	Register struct {
		client *clientv3.Client
		kv     clientv3.KV
		lease  clientv3.Lease

		localIP string //本机IP
	}
)

var (
	G_register *Register
)

func getLocalIP() (ipv4 string, err error) {
	var (
		addrs     []net.Addr
		addr      net.Addr
		ipNet     *net.IPNet
		isIPNet   bool
		isLocalIP bool
	)

	if addrs, err = net.InterfaceAddrs(); err != nil {
		return
	}

	// 取第一个非io的网卡IP
	for _, addr = range addrs {
		if ipNet, isIPNet = addr.(*net.IPNet); isIPNet && !ipNet.IP.IsLoopback() {
			// 跳过IPv6
			if ipNet.IP.To4() != nil {
				ipv4 = ipNet.IP.String()
				if isLocalIP = strings.HasPrefix(ipv4, "192.168.1."); isLocalIP {
					fmt.Println("注册的本机IP=", ipv4)
					return
				}
			}
		}
	}
	err = common.ERR_NO_LOCAL_IP_FOUND
	return
}

func InitRegister() (err error) {
	var (
		config  clientv3.Config
		client  *clientv3.Client
		kv      clientv3.KV
		lease   clientv3.Lease
		localIp string
	)

	config = clientv3.Config{
		Endpoints:   G_config.EtcdEndpoints, // 集群地址
		DialTimeout: time.Duration(G_config.EtcdDailTimeout) * time.Millisecond,
	}

	if client, err = clientv3.New(config); err != nil {
		return
	}

	kv = clientv3.NewKV(client)
	lease = clientv3.NewLease(client)

	if localIp, err = getLocalIP(); err != nil {
		return
	}

	G_register = &Register{
		client:  client,
		kv:      kv,
		lease:   lease,
		localIP: localIp,
	}

	// 注册worker服务
	go G_register.KeepOnline()

	return
}

// 注册到etcd的 /cron/worker/IP地址 ,并自动续租
func (register *Register) KeepOnline() {
	var (
		regKey         = common.JOB_WORKER_DIR + register.localIP
		err            error
		leaseGrantResp *clientv3.LeaseGrantResponse
		keepRespChan   <-chan *clientv3.LeaseKeepAliveResponse
		keepAliveResp  *clientv3.LeaseKeepAliveResponse
		cancelCtx      context.Context
		cancelFunc     context.CancelFunc
	)

	for {
		cancelFunc = nil

		if leaseGrantResp, err = register.lease.Grant(context.TODO(), 10); err != nil {
			goto RETRY
		}
		// 自动续租
		if keepRespChan, err = register.lease.KeepAlive(context.TODO(), leaseGrantResp.ID); err != nil {
			goto RETRY
		}

		cancelCtx, cancelFunc = context.WithCancel(context.TODO())
		if _, err = register.kv.Put(cancelCtx, regKey, "", clientv3.WithLease(leaseGrantResp.ID)); err != nil {
			goto ERR
		}

		// 处理续租应答
		for {
			select {
			case keepAliveResp = <-keepRespChan:
				if keepAliveResp == nil {
					goto RETRY
				}
			}
		}

	RETRY: // 继续尝试连接
		time.Sleep(1 * time.Second)
		continue
	ERR: // 错误就退出
		if cancelFunc != nil {
			cancelFunc()
		}
	}
}
